package com.daikun.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.simple.SimpleDataSource;
import com.daikun.common.enums.BaseResultEnum;
import com.daikun.common.exception.BizException;
import com.daikun.common.model.TableConvertModel;
import lombok.SneakyThrows;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 当前工具类只适用于编译期
 * 将数据库表生成对应实体类，xml 文件，dao 接口
 * @author earl created 2025-07-08
 * @version init 0.0.1
 */
public class TableConvertUtil {

    @SneakyThrows
    public static String tableToXml(TableConvertModel.TableToXmlModel model) {
        model.checkParam();
        // 生成的内容是否输出到指定文件中
        boolean isOut = StrUtil.isNotBlank(model.getXmlFilePath());
        // 若需要输出到指定文件中，则判断文件是否存在，若文件不存在，则创建
        if (isOut && !FileUtil.exist(model.getXmlFilePath())) {
            FileUtil.newFile(model.getXmlFilePath());
        }

        StringBuilder result = new StringBuilder();
        try (SimpleDataSource ds = new SimpleDataSource(model.getUrl(), model.getUsername(), model.getPassword())) {
            // 查询表的主键信息
            List<Entity> primaryEntity = Db.use(ds).query("show keys from " + model.getTableName() + " where Key_name = 'primary';");
            String primaryColumnName;
            if (CollUtil.isNotEmpty(primaryEntity) && primaryEntity.size() == 1) {
                primaryColumnName = primaryEntity.get(0).getStr("column_name");
            } else {
                throw new BizException(BaseResultEnum.PARAM_ERROR, "当前表不存在主键，无法进行生成");
            }

            // 查询表的字段信息
            List<Entity> list = Db.use(ds).query("select column_name, data_type, column_comment from information_schema.columns where table_schema = '" + model.getDatabase() + "' and table_name = '" + model.getTableName() + "';");

            // 生成文件头
            result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append("\n");
            result.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">").append("\n");
            result.append("<!--suppress SqlNoDataSourceInspection -->").append("\n");
            result.append("<mapper namespace=\"com.xxx.xxx.xxxDao\">").append("\n\n");

            // 生成文件中 数据库字段 和 实体类 字段得映射
            result.append("\t").append("<resultMap id=\"resp\" type=\"").append(model.getClazz().getName()).append("\">").append("\n");
            for (Entity entity : list) {
                String dbFieldName = entity.getStr("column_name").toLowerCase();
                String entityFieldName = StrUtil.toCamelCase(dbFieldName);
                if (dbFieldName.equals(primaryColumnName)) {
                    result.append("\t\t").append("<id column=\"").append(dbFieldName).append("\" property=\"").append(entityFieldName).append("\"/>").append("\n");
                } else {
                    result.append("\t\t").append("<result column=\"").append(dbFieldName).append("\" property=\"").append(entityFieldName).append("\"/>").append("\n");
                }
            }
            result.append("\t").append("</resultMap>").append("\n\n");

            // 生成数据库字段列
            StringBuilder sb = new StringBuilder();
            result.append("\t").append("<sql id=\"cols\">").append("\n");
            for (Entity entity : list) {
                String dbFieldName = entity.getStr("column_name").toLowerCase();
                sb.append(dbFieldName).append(", ");
                if (sb.length() > 90) {
                    result.append("\t\t").append(sb, 0, sb.length() - 1).append("\n");
                    sb = new StringBuilder();
                }
            }
            if (sb.length() > 0) {
                result.append("\t\t").append(sb, 0, sb.length() - 2).append("\n");
            } else {
                result = new StringBuilder(result.substring(0, result.length() - 2));
            }
            result.append("\t").append("</sql>").append("\n\n");

            // 生成添加语句
            result.append("\t").append("<insert id=\"saveOne\" useGeneratedKeys=\"true\">").append("\n");
            result.append("\t\t").append("insert into ").append(model.getTableName()).append("\n");
            result.append("\t\t").append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">").append("\n");
            for (Entity entity : list) {
                String dbFieldName = entity.getStr("column_name").toLowerCase();
                if (dbFieldName.equals(primaryColumnName)) {
                    continue;
                }
                String entityFieldName = StrUtil.toCamelCase(dbFieldName);
                String javaType = dbColumnTypeToJavaFieldType(entity.getStr("data_type")).getKey();
                if ("String".equals(javaType)) {
                    result.append("\t\t\t").append("<if test=\"").append(entityFieldName).append(" != null and ").append(entityFieldName).append(" != ''").append("\">")
                            .append(dbFieldName).append(",</if>").append("\n");
                } else {
                    result.append("\t\t\t").append("<if test=\"").append(entityFieldName).append(" != null\">")
                            .append(dbFieldName).append(",</if>").append("\n");
                }
            }
            result.append("\t\t").append("</trim>").append("\n");
            result.append("\t\t").append("<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">").append("\n");
            for (Entity entity : list) {
                String dbFieldName = entity.getStr("column_name").toLowerCase();
                if (dbFieldName.equals(primaryColumnName)) {
                    continue;
                }
                String entityFieldName = StrUtil.toCamelCase(dbFieldName);
                String javaType = dbColumnTypeToJavaFieldType(entity.getStr("data_type")).getKey();
                if ("String".equals(javaType)) {
                    result.append("\t\t\t").append("<if test=\"").append(entityFieldName).append(" != null and ").append(entityFieldName).append(" != ''").append("\">")
                            .append("#{").append(entityFieldName).append("}").append(",</if>").append("\n");
                } else {
                    result.append("\t\t\t").append("<if test=\"").append(entityFieldName).append(" != null\">")
                            .append("#{").append(entityFieldName).append("}").append(",</if>").append("\n");
                }
            }
            result.append("\t\t").append("</trim>").append("\n");
            result.append("\t").append("</insert>").append("\n\n");

            // 生成批量添加语句
            result.append("\t").append("<insert id=\"batchSave\" parameterType=\"").append(model.getClazz().getName()).append("\">").append("\n");
            result.append("\t\t").append("insert into ").append(model.getTableName()).append("(<include refid=\"cols\"/>) values").append("\n");
            result.append("\t\t").append("<foreach collection=\"list\" item=\"e\" open=\"(\" close=\")\" separator=\"),(\">").append("\n");
            sb = new StringBuilder();
            for (int i = 0; i < list.size(); i++) {
                if (i == list.size() - 1) {
                    if (list.get(i).getStr("column_name").equals(primaryColumnName)) {
                        sb.append("null");
                    } else {
                        sb.append("#{e.").append(StrUtil.toCamelCase(list.get(i).getStr("column_name").toLowerCase())).append("}");
                    }
                } else {
                    if (list.get(i).getStr("column_name").equals(primaryColumnName)) {
                        sb.append("null, ");
                    } else {
                        sb.append("#{e.").append(StrUtil.toCamelCase(list.get(i).getStr("column_name").toLowerCase())).append("}, ");
                    }
                }
                if (sb.length() > 105) {
                    result.append("\t\t\t").append(sb).append("\n");
                    sb = new StringBuilder();
                }
            }
            if (sb.length() > 0) {
                result.append("\t\t\t").append(sb).append("\n");
            }
            result.append("\t\t").append("</foreach>").append("\n");
            result.append("\t").append("</insert>").append("\n\n");

            // 生成修改语句
            result.append("\t").append("<update id=\"update\" parameterType=\"").append(model.getClazz().getName()).append("\">").append("\n");
            result.append("\t\t").append("update ").append(model.getTableName()).append("\n");
            result.append("\t\t").append("<trim prefix=\"set\" suffixOverrides=\",\">").append("\n");
            for (Entity entity : list) {
                String dbFieldName = entity.getStr("column_name").toLowerCase();
                if (dbFieldName.equals(primaryColumnName)) {
                    continue;
                }
                String entityFieldName = StrUtil.toCamelCase(dbFieldName);
                String javaType = dbColumnTypeToJavaFieldType(entity.getStr("data_type")).getKey();
                if ("String".equals(javaType)) {
                    result.append("\t\t\t").append("<if test=\"").append(entityFieldName).append(" != null and ").append(entityFieldName).append(" != ''\">")
                            .append(dbFieldName).append(" = #{").append(entityFieldName).append("},</if>\n");
                } else {
                    result.append("\t\t\t").append("<if test=\"").append(entityFieldName).append(" != null").append("\">")
                            .append(dbFieldName).append(" = #{").append(entityFieldName).append("},</if>\n");
                }
            }
            result.append("\t\t").append("</trim>").append("\n");
            result.append("\t\t").append("where ").append(primaryColumnName.toLowerCase()).append(" = #{").append(StrUtil.toCamelCase(primaryColumnName.toLowerCase())).append("}\n");
            result.append("\t").append("</update>").append("\n\n");

            result.append("</mapper>").append("\n");
        }
        if (isOut) {
            FileUtil.writeString(result.toString(), model.getXmlFilePath(), StandardCharsets.UTF_8);
            return "success";
        } else {
            return result.toString();
        }
    }


    public static void main(String[] args) {
        System.out.println(StrUtil.toCamelCase("CAMPAIGN"));
    }

    @SneakyThrows
    public static String tableToBean(TableConvertModel.TableToBeanModel model) {
        model.checkParam();
        StringBuilder result = new StringBuilder();
        try (SimpleDataSource ds = new SimpleDataSource(model.getUrl(), model.getUsername(), model.getPassword())) {
            List<Entity> list = Db.use(ds).query("select column_name, data_type, column_comment from information_schema.columns where table_schema = '" + model.getDatabase() + "' and table_name = '" + model.getTableName() + "';");
            Set<String> set = new HashSet<>();
            result.append("import lombok.Getter;\n");
            result.append("import lombok.Setter;\n\n");
            List<Pair<String, String>> codeList = new ArrayList<>();
            for (Entity entity : list) {
                String fieldName = entity.getStr("column_name").toLowerCase();
                fieldName = StrUtil.toCamelCase(fieldName);
                Pair<String, String> pair = dbColumnTypeToJavaFieldType(entity.getStr("data_type"));
                String fieldType = pair.getKey();
                if (StrUtil.isNotBlank(pair.getValue())) {
                    set.add(pair.getValue());
                }
                String fieldComment = entity.getStr("column_comment");
                fieldComment = StrUtil.isBlank(fieldComment) ? "" : (fieldComment.contains("\n") ? fieldComment.replace("\n", " -> ") : fieldComment);
                codeList.add(Pair.of("\t/** " + fieldComment + " */\n", "\tprivate " + fieldType + " " + fieldName + ";\n"));
            }
            for (String s : set) {
                result.append(s).append("\n");
            }

            result.append(CollUtil.isEmpty(set) ? "" : "\n");
            result.append("/**\n * from table ").append(model.getDatabase().toLowerCase()).append(".").append(model.getTableName().toLowerCase()).append("\n * @author system created ").append(DateUtil.date().toDateStr()).append("\n * @version 0.0.1 init\n */\n");
            result.append("@Getter\n@Setter\n");
            String tableName = StrUtil.isBlank(model.getEntityName()) ? (StrUtil.toCamelCase(model.getTableName().toLowerCase()) + "Entity") : model.getEntityName();
            result.append("public class ").append(tableName.substring(0, 1).toUpperCase()).append(tableName.substring(1)).append(" {\n\n");
            for (Pair<String, String> item : codeList) {
                result.append(item.getKey()).append(item.getValue());
            }
            result.append("\n}\n");
        }
        return result.toString();
    }

    /**
     * 将数据库中的字段类型，映射为 Java 中的字段类型
     * @param dbColumnType 数据库字段类型
     * @return key 为当前字段的 Java 类型，val 如果有值，则为需要引入的依赖
     */
    private static Pair<String, String> dbColumnTypeToJavaFieldType(String dbColumnType) {
        switch (dbColumnType.toLowerCase()) {
            case "int":
            case "smallint":
            case "tinyint":
            case "mediumint":
                return Pair.of("Integer", null);
            case "bigint":
                return Pair.of("Long", null);
            case "float":
                return Pair.of("Float", null);
            case "double":
                return Pair.of("Double", null);
            case "boolean":
                return Pair.of("Boolean", null);
            case "decimal":
                return Pair.of("BigDecimal", "import java.math.BigDecimal;");
            case "date":
            case "time":
            case "year":
            case "datetime":
            case "timestamp":
                return Pair.of("Date", "import java.util.Date;");
            case "binary":
            case "varbinary":
            case "blob":
            case "longblob":
                return Pair.of("Byte[]", null);
        }
        return Pair.of("String", null);
    }

    /**
     * 获取生成实体类所需的参数对象
     * @param dbName 数据库名称 必传
     * @param tableName 要生成的表名称 必传
     * @param modelName 生成的实体类名称，若为空，则默认生成的实体类以 驼峰表名称 + Entity 命名
     * @param item 对应的国家信息，必传
     */
    public static TableConvertModel.TableToBeanModel getTableToBeanModel(String dbName, String tableName, String modelName, TableConvertModel.TableConvertCountryEnum item) {
        if (StrUtil.isBlank(tableName) || item == null) {
            throw new BizException(BaseResultEnum.PARAM_ERROR);
        }
        TableConvertModel.TableToBeanModel result = new TableConvertModel.TableToBeanModel(null, tableName, null, "yewu-test", "a389pDaikQfg5f0Tto", modelName);
        dbName = StrUtil.blankToDefault(dbName, item.getDefDbName());
        result.setUrl(item.getDefDbHost() + "/" + dbName + "?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true");
        return result;
    }

    /**
     * 获取生成 MyBatis XML 文件所需的参数对象
     * @param dbName 数据库名称，必传
     * @param tableName 表名称，必传
     * @param filePath 要生成的文件路径，D:/a/b/c/aaa.xml
     * @param clazz 当前表对应的实体类 class 对象
     * @param item 对应的国家标识，必传
     */
    public static TableConvertModel.TableToXmlModel getTableToXmlModel(String dbName, String tableName, String filePath, Class<?> clazz, TableConvertModel.TableConvertCountryEnum item) {
        TableConvertModel.TableToBeanModel model = getTableToBeanModel(dbName, tableName, null, item);
        return new TableConvertModel.TableToXmlModel(model, filePath, clazz);
    }

}
